 /*******************************************************************************
  * Copyright (c) 2005, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/

 package org.eclipse.ui.internal.cheatsheets.composite.model;

 import java.util.ArrayList ;
 import java.util.List ;

 import org.eclipse.ui.internal.provisional.cheatsheets.ICompositeCheatSheetTask;
 import org.eclipse.ui.internal.provisional.cheatsheets.ITaskGroup;

 public class SuccesorTaskFinder {
     
     private AbstractTask currentTask;
     ICompositeCheatSheetTask bestLaterTask;
     ICompositeCheatSheetTask bestEarlierTask;
     private boolean seenThisTask;

     public SuccesorTaskFinder(ICompositeCheatSheetTask task) {
         currentTask = (AbstractTask)task;
     }
     
     /**
      * Find the next recommended task or tasks to be completed.
      * Algorithm - visualize the tree as having its root at the top,
      * children below and to the left of their parents and then
      * search the tree from left to right. Look for
      * the best predecessor which is the first task to the
      * left of this task that is runnable and the best successor
      * which is the first task to the
      * right of this task which is runnable.
      * @param task The task which was just completed
      * @return An array of tasks which can be started
      */
     public ICompositeCheatSheetTask[] getRecommendedSuccessors()
     {
         // TODO this code could be moved to TaskGroup
 if (ITaskGroup.CHOICE.equals(currentTask.getKind())) {
             // For a choice if more than one child is runnable return it
 List runnableChoices = findRunnableChoices();
             if (runnableChoices.size() != 0) {
                 return (ICompositeCheatSheetTask[])runnableChoices.toArray
                 ( new ICompositeCheatSheetTask[runnableChoices.size()]);
             }
         }
         return getBestSuccessor();
     }

     private List findRunnableChoices() {
         List result;
         result = new ArrayList ();
         if (isStartable(currentTask)) {
             ICompositeCheatSheetTask[] subtasks = currentTask.getSubtasks();
             for (int i = 0; i < subtasks.length; i++) {
                 if (isStartable(subtasks[i])) {
                     result.add(subtasks[i]);
                 }
             }
         }
         return result;
     }

     private boolean isStartable(ICompositeCheatSheetTask task) {
         int state = task.getState();
         return (state != ICompositeCheatSheetTask.COMPLETED &&
                 state != ICompositeCheatSheetTask.SKIPPED &&
                 task.requiredTasksCompleted());
     }

     private ICompositeCheatSheetTask[] getBestSuccessor() {
         bestLaterTask = null;
         bestEarlierTask = null;
         seenThisTask = false;
         searchRunnableChildren(currentTask.getCompositeCheatSheet().getRootTask());
         // If there is a task which is found later in the tree return
 // that, otherwise an earlier task.
 if (bestLaterTask != null) {
             return new ICompositeCheatSheetTask[] {bestLaterTask};
         }
         if (bestEarlierTask != null) {
             return new ICompositeCheatSheetTask[] {bestEarlierTask};
         }
         return new ICompositeCheatSheetTask[0];
     }

     private void searchRunnableChildren(ICompositeCheatSheetTask task) {
         // Don't search completed tasks or their children
 // and stop searching if we've already found the best successor
 if (bestLaterTask != null) {
             return;
         }
         if (task == currentTask) {
             seenThisTask = true;
         }
         if (task.getState() == ICompositeCheatSheetTask.COMPLETED ||
             task.getState() == ICompositeCheatSheetTask.SKIPPED ) {
             if (isTaskAncestor(task, currentTask)) {
                 seenThisTask = true;
             }
             return;
         }
         
         if ( isStartable(task) && task != currentTask) {
             if (seenThisTask) {
                 if (bestLaterTask == null) {
                     bestLaterTask = task;
                 }
             } else {
                 if (bestEarlierTask == null) {
                     bestEarlierTask = task;
                 }
             }
         }

         ICompositeCheatSheetTask[] subtasks = task.getSubtasks();
         for (int i = 0; i < subtasks.length; i++) {
             searchRunnableChildren(subtasks[i]);
         }

     }

     private boolean isTaskAncestor(ICompositeCheatSheetTask ancestorCandididate, ICompositeCheatSheetTask task) {
         ICompositeCheatSheetTask nextTask = task;
         while (nextTask != null) {
             if (nextTask == ancestorCandididate) {
                 return true;
             }
             nextTask = nextTask.getParent();
         }
         return false;
     }
 }

